package com.keyidea.jfxui.core.config.redis;

import com.keyidea.jfxui.core.server.utils.ByteUtils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.connection.MessageListener;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.ChannelTopic;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.stereotype.Component;

import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

/**
 * redis工具类
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class RedisUtil {

    private final RedisTemplate<String, byte[]> redisTemplateByte;

    private final RedisMessageListenerContainer container;

    private final StringRedisSerializer stringRedisSerializer;
    private final Map<String, MessageListener> registeredListener = new HashMap<>();

    /**
     * 发布
     *
     * @param channel 频道
     * @param body    消息
     * @return 发布结果
     */
    public String publish(String channel, String body) {
        redisTemplateByte.convertAndSend(channel, body.getBytes(StandardCharsets.UTF_8));
        return Result.OK.getType();
    }

    /**
     * 发布
     *
     * @param channel 频道
     * @param body    消息
     * @return 发布结果
     */
    public String publish(String channel, byte[] body) {
        return publish(channel, body, 0);
    }

    /**
     * 发布（带重试次数）
     *
     * @param channel    频道
     * @param body       消息
     * @param retryCount 重试次数
     * @return 发布结果
     */
    public String publish(String channel, byte[] body, int retryCount) {
        Long l = redisTemplateByte.convertAndSend(channel, body);
        String result;
        if (l == null || l == 0) {
            result = Result.NOK.getType();
            if (retryCount < 3) {
                retryCount++;
                publish(channel, body, retryCount);
            } else {
                log.error("redis 发送失败, 消息体为：{}", ByteUtils.bytesToHex(body));
            }
        } else {
            result = Result.OK.getType();
        }
        return result;
    }

    /**
     * 订阅
     *
     * @param channel 频道
     * @return 订阅结果
     */
    public String subscribe(String channel) {
        MessageListener listener = registeredListener.computeIfAbsent(channel, ch -> new PrintMessageListener(stringRedisSerializer));
        container.addMessageListener(listener, new ChannelTopic(channel));
        return Result.OK.getType();
    }

    /**
     * 取消订阅
     *
     * @param channel 频道
     * @return 取消结果
     */
    public String unsubscribe(String channel) {
        MessageListener messageListener = registeredListener.get(channel);
        if (messageListener != null) {
            container.removeMessageListener(messageListener, new ChannelTopic(channel));
        }
        return Result.OK.getType();
    }

    @Getter
    enum Result {
        OK("ok"),
        NOK("notOk");

        private final String type;

        Result(String type) {
            this.type = type;
        }
    }
}
